package com.opencee.cloud.base.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.opencee.boot.db.mybatis.service.impl.SupperServiceImpl;
import com.opencee.cloud.base.constants.BaseConstants;
import com.opencee.cloud.base.entity.BaseApplicationEntity;
import com.opencee.cloud.base.mapper.BaseApplicationMapper;
import com.opencee.cloud.base.service.IBaseApplicationService;
import com.opencee.cloud.base.vo.BaseApplicationDetailsVO;
import com.opencee.cloud.base.vo.BaseApplicationResultVO;
import com.opencee.common.exception.BaseFailException;
import com.opencee.common.security.SecurityClient;
import com.opencee.common.utils.RedisTemplateUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 应用信息 服务实现类
 * </p>
 *
 * @author liuyadu
 * @since 2021-04-16
 */
@Service
public class BaseApplicationServiceImpl extends SupperServiceImpl<BaseApplicationMapper, BaseApplicationEntity> implements IBaseApplicationService {

    @Autowired
    private RedisTemplateUtil redisTemplateUtil;
    @Autowired
    private JdbcClientDetailsService jdbcClientDetailsService;

    /**
     * token有效期，默认12小时
     */
    public static final int ACCESS_TOKEN_VALIDITY_SECONDS = 60 * 60 * 12;
    /**
     * token有效期，默认7天
     */
    public static final int REFRESH_TOKEN_VALIDITY_SECONDS = 60 * 60 * 24 * 7;

    /**
     * 查询app和应用信息
     *
     * @param appId
     * @return
     */
    @Override
    public BaseApplicationDetailsVO getByAppId(Long appId) {
        String key = BaseConstants.CACHE_APPLICATION_APP_ID + appId;
        if (redisTemplateUtil.hasKey(key)) {
            return (BaseApplicationDetailsVO) redisTemplateUtil.get(key);
        }
        BaseApplicationEntity baseApplicationEntity = getById(appId);
        BaseApplicationDetailsVO appDetails = null;
        if (baseApplicationEntity != null) {
            appDetails = new BaseApplicationDetailsVO();
            BeanUtils.copyProperties(baseApplicationEntity, appDetails);
            BaseClientDetails clientDetails = (BaseClientDetails) jdbcClientDetailsService.loadClientByClientId(baseApplicationEntity.getAppKey());
            SecurityClient client = new SecurityClient(clientDetails);
            client.setAutoApproveScopes(clientDetails.getAutoApproveScopes());
            appDetails.setClient(client);
            redisTemplateUtil.set(key, appDetails, 2, TimeUnit.HOURS);
        } else {
            redisTemplateUtil.set(key, appDetails, 1, TimeUnit.MINUTES);
        }
        return appDetails;
    }

    /**
     * 根据appKey查询详情(包含oauth2开发信息)
     *
     * @param appKey
     * @return
     */
    @Override
    public BaseApplicationDetailsVO getByAppKey(String appKey) {
        String key = BaseConstants.CACHE_APPLICATION_APP_KEY + appKey;
        if (redisTemplateUtil.hasKey(key)) {
            return (BaseApplicationDetailsVO) redisTemplateUtil.get(key);
        }
        QueryWrapper<BaseApplicationEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(BaseApplicationEntity::getAppKey, appKey);
        BaseApplicationEntity baseApplicationEntity = this.getOne(queryWrapper);
        BaseApplicationDetailsVO appDetails = null;
        if (baseApplicationEntity != null) {
            appDetails = new BaseApplicationDetailsVO();
            BeanUtils.copyProperties(baseApplicationEntity, appDetails);
            BaseClientDetails clientDetails = (BaseClientDetails) jdbcClientDetailsService.loadClientByClientId(baseApplicationEntity.getAppKey());
            SecurityClient client = new SecurityClient(clientDetails);
            client.setAutoApproveScopes(clientDetails.getAutoApproveScopes());
            appDetails.setClient(client);
            redisTemplateUtil.set(key, appDetails, 2, TimeUnit.HOURS);
        } else {
            redisTemplateUtil.set(key, appDetails, 1, TimeUnit.MINUTES);
        }
        return appDetails;
    }

    /**
     * 添加应用
     *
     * @param app
     * @return 应用息
     */
    @Override
    public BaseApplicationResultVO saveApp(BaseApplicationDetailsVO app) {
        if (exists(app.getAppName())) {
            throw new BaseFailException("应用名称已存在");
        }
        Assert.hasText(app.getSourceId(), "来源标识不能为空");
        String appKey = RandomUtil.randomString(16);
        String appSecret = RandomUtil.randomString(32);
        app.setAppKey(appKey);
        app.setAppSecret(appSecret);
        app.setDeleted(0);
        app.setCreateTime(new Date());
        app.setUpdateTime(app.getCreateTime());

        boolean flag = super.save(app);
        // 功能授权
        SecurityClient client = app.getClient();
        if (client == null) {
            client = new SecurityClient();
        }

        client.setClientId(app.getAppKey());
        client.setClientSecret(app.getAppSecret());
        if (client.getScope().isEmpty()) {
            client.setScope(Arrays.asList(new String[]{"user_info"}));
        }

        if (client.getAuthorizedGrantTypes().isEmpty()) {
            client.setAuthorizedGrantTypes(Arrays.asList("authorization_code", "refresh_token"));
        }
        if (client.getAccessTokenValiditySeconds() == null) {
            client.setAccessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS);
        }
        if (client.getRefreshTokenValiditySeconds() == null) {
            client.setRefreshTokenValiditySeconds(REFRESH_TOKEN_VALIDITY_SECONDS);
        }
        jdbcClientDetailsService.addClientDetails(client);
        BaseApplicationResultVO result = new BaseApplicationResultVO();
        result.setAppId(app.getAppId());
        result.setAppKey(app.getAppKey());
        result.setAppSecret(app.getAppSecret());
        result.setSourceId(app.getSourceId());
        return result;
    }

    /**
     * 修改应用
     *
     * @param app 应用
     * @return 应用信息
     */
    @Override
    public BaseApplicationResultVO updateApp(BaseApplicationDetailsVO app) {
        BaseApplicationEntity saved = getById(app.getAppId());
        if (saved == null) {
            throw new BaseFailException("应用不存在");
        }
        if (!saved.getAppName().equals(app.getAppName())) {
            // 和原来不一致重新检查唯一性
            if (exists(app.getAppName())) {
                throw new BaseFailException("应用名称已存在");
            }
        }
        app.setAppSecret(null);
        app.setSourceId(null);
        boolean flag = super.updateById(app);
        SecurityClient client = app.getClient();
        if (client != null) {
            client.setClientId(saved.getAppKey());
            Assert.hasText(client.getClientId(), "客户端ID不能为空");
            if (client.getAccessTokenValiditySeconds() == null) {
                client.setAccessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS);
            }
            if (client.getRefreshTokenValiditySeconds() == null) {
                client.setRefreshTokenValiditySeconds(REFRESH_TOKEN_VALIDITY_SECONDS);
            }
            jdbcClientDetailsService.updateClientDetails(client);
        }
        redisTemplateUtil.del(BaseConstants.CACHE_APPLICATION_APP_ID + saved.getAppId());
        redisTemplateUtil.del(BaseConstants.CACHE_APPLICATION_APP_KEY + saved.getAppKey());
        BaseApplicationResultVO result = new BaseApplicationResultVO();
        result.setAppId(saved.getAppId());
        result.setAppKey(saved.getAppKey());
        result.setAppSecret(saved.getAppSecret());
        result.setSourceId(saved.getSourceId());
        return result;
    }

    /**
     * 重置秘钥
     *
     * @param appId
     * @return
     */
    @Override
    public String restSecret(String appId) {
        BaseApplicationEntity entity = getById(appId);
        if (entity == null) {
            throw new BaseFailException(appId + "应用不存在!");
        }
        // 生成新的密钥
        String appSecret = RandomUtil.randomString(32);
        entity.setAppSecret(appSecret);
        updateById(entity);
        jdbcClientDetailsService.updateClientSecret(entity.getAppSecret(), appSecret);
        redisTemplateUtil.del(BaseConstants.CACHE_APPLICATION_APP_ID + entity.getAppId());
        redisTemplateUtil.del(BaseConstants.CACHE_APPLICATION_APP_KEY + entity.getAppKey());
        return appSecret;
    }

    /**
     * 检查应用是否存在
     *
     * @param appName
     * @return
     */
    @Override
    public boolean exists(String appName) {
        QueryWrapper<BaseApplicationEntity> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(BaseApplicationEntity::getAppName, appName);
        return baseMapper.selectCount(wrapper) > 0;
    }

    /**
     * 移除应用
     *
     * @param id
     * @return
     */
    @Override
    public boolean removeById(Serializable id) {
        BaseApplicationEntity entity = getByAppId((Long) id);
        Assert.notNull(entity, "应用不存在");
        redisTemplateUtil.del(BaseConstants.CACHE_APPLICATION_APP_ID + entity.getAppId());
        redisTemplateUtil.del(BaseConstants.CACHE_APPLICATION_APP_KEY + entity.getAppKey());
        jdbcClientDetailsService.removeClientDetails(entity.getAppKey());
        return super.removeById(id);
    }

}
